package com.hongshan.wallet.service

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Binder
import android.os.Build
import android.support.v4.app.NotificationCompat
import com.hongshan.wallet.Constant
import com.hongshan.wallet.MyApplication
import com.hongshan.wallet.R
import com.hongshan.wallet.http.HttpClient
import com.hongshan.wallet.http.HttpResult
import com.hongshan.wallet.log
import com.hongshan.wallet.router.RouterIndex
import com.hongshan.wallet.router.go
import com.hongshan.wallet.toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import java.io.EOFException
import java.net.ProtocolException
import java.net.Proxy
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import javax.net.ssl.SSLException


/**
 * Copyright Zhangxx.
 * Copyright (c) 2019
 * All rights reserved
 *
 * @project  : Wallet
 * @package  : com.example.wallet.service
 * @author   : zhangxx
 * @date     : 2019/4/2
 * @email    : qq957413212@hotmail.com
 * @desc     : //TODO
 */
class WebSocketService : Service() {

    val binder = WebSocketBinder()

    val builder by lazy {
        OkHttpClient.Builder()
            .connectTimeout(HttpClient.CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
            .readTimeout(HttpClient.READ_TIME_OUT, TimeUnit.MILLISECONDS)
            .writeTimeout(HttpClient.WRITE_TIME_OUT, TimeUnit.MILLISECONDS)
            .pingInterval(10, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true)
            .proxy(Proxy.NO_PROXY)
            .build()
    }

    val request by lazy { Request.Builder().url(Constant.HttpApi.BASE_WEBSOCKET_URL).build() }

    var socket: WebSocket? = null

    val listener = MyWebSocketListener()
    var job: Job? = null
    var pingStatus = true
    //        var connectFailCount = 0
    var result = false
    var onReceiveListener: ((String) -> Unit)? = null
    var killSelf = false
    val noticfitionBuild by lazy { getNoticfitionBuild(resources.getString(R.string.appName), "", BitmapFactory.decodeResource(resources, R.mipmap.logo)) }

    fun getNoticfitionBuild(title: String, content: String, logo: Bitmap?) =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val chan = NotificationChannel(
                "service",
                "服务",
                NotificationManager.IMPORTANCE_HIGH
            )
            chan.enableLights(true)
            chan.lightColor = Color.RED
            chan.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
            chan.importance = NotificationManager.IMPORTANCE_HIGH
            val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            manager.createNotificationChannel(chan)
            val builder = NotificationCompat.Builder(this, "service")
            builder
                .setSmallIcon(R.mipmap.logo)
                .setContentText(content)
                .setSubText(title)
                .setLargeIcon(logo)
                .setContentTitle(title)
                .setOnlyAlertOnce(true)
                .setPriority(Notification.PRIORITY_DEFAULT)
                .setAutoCancel(false)
        } else {
            NotificationCompat.Builder(MyApplication.applicationContext)
                .setContentTitle(title)
                .setOnlyAlertOnce(true)
                .setContentText(content)
                .setSmallIcon(android.R.drawable.stat_notify_more)
                .setAutoCancel(false)
        }

    override fun onBind(intent: Intent) = binder

    override fun onCreate() {
        super.onCreate()
        log("service onCreate")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        log("service onStartCommand")
        startForeground(Notification_ID, noticfitionBuild.build())
        killSelf = false
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        killSelf = true
        stopPing()
        stopWxClient()
    }


    fun startWsClient() {
        this.stopPing()
        this.stopWxClient()
        socket = builder.newWebSocket(request, listener)
    }

    fun stopWxClient() {
        socket?.close(1001, "Service退出")
        socket?.cancel()
        socket = null
    }

    fun startPing() {
        pingStatus = true
        job?.cancel()
        job = GlobalScope.launch(Dispatchers.IO) {
            while (pingStatus && socket != null) {
                delay(8000)
                val ping = "{\"type\":\"ping\"}"
                result = send(ping)
            }
        }
    }

    fun send(text: String?): Boolean {
        if (text != null) {
            val result = socket?.send(text)
            log("send: $text result: $result")
            return result ?: false
        }
        return false
    }

    fun stopPing() {
        pingStatus = false
        job?.cancel()
    }

    private fun reStartClient() {
        GlobalScope.launch {
            delay(10000)
            log("restart")
            this@WebSocketService.startWsClient()

        }
    }

    inner class WebSocketBinder : Binder() {
        fun getService() = this@WebSocketService
    }


    inner class MyWebSocketListener : WebSocketListener() {
        override fun onOpen(webSocket: WebSocket, response: Response) {
            super.onOpen(webSocket, response)
            val source = response.body()?.source()
            source?.request(Long.MAX_VALUE)
            val string = source?.buffer()?.clone()?.readString(Charset.forName("UTF-8"))
            log("onOpen:$string")
            noticfitionBuild.setContentText("已连接...")
            startForeground(Notification_ID, noticfitionBuild.build())
        }

        override fun onMessage(webSocket: WebSocket, text: String) {
            super.onMessage(webSocket, text)
            log("onMessage:$text")
//            connectFailCount = 0
            val msg = Constant.gson.fromJson(text, HttpResult::class.java)
            when {
                msg.type == "init" -> {
                    val login =
                        "{\"type\":\"login\",\"token\":\"${MyApplication.user?.token}\",\"investor_id\":\"${MyApplication.user?.account_id}\"}"
                    send(login)
                    startPing()
                }
                msg.type == "message" -> {
                    toast(msg.msg)
                    if (msg.code in arrayOf(43001, 43002, 43004, 43005, -4001)) {
                        MyApplication.user?.clean()
                        killSelf = true
                        stopPing()
                        stopWxClient()
                        stopSelf()
                        go(RouterIndex.LOGIN)
                    }
                }
                msg.type == "close" -> {
                    socket?.close(1001, "Service退出")
                    socket?.cancel()
                }
                else -> onReceiveListener?.invoke(text)
            }
        }

        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
            super.onClosed(webSocket, code, reason)
            log("onClose code: $code reason: $reason")
        }

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
            super.onFailure(webSocket, t, response)
            log("onFailure")
            noticfitionBuild.setContentText("已断开")
            startForeground(Notification_ID, noticfitionBuild.build())
            t.printStackTrace()
            if (pingStatus) {
                stopPing()
            }
            if ((t is EOFException || t is UnknownHostException || t is TimeoutException || t is SSLException || t is SocketTimeoutException || t is ProtocolException) && !killSelf) {
                webSocket.cancel()
                reStartClient()
            }
        }

    }

    companion object{
        val Notification_ID = 100
    }
}